﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once

#include "GcmpModel.h"

namespace gcmp
{
    class IDocument;
    struct UndoRedoEntityBrief;

    /// \brief 确认撤消接口
    ///
    /// 通常撤消后可以重做，不需要提请用户确认。
    /// 但撤消后不能重做的，需要提请用户确认。
    /// \see UndoRedoUtils::Undo()
    class GCMP_MODEL_EXPORT IUndoConfirm
    {
    public:
        /// \brief 当撤消的类型是撤消后就不能重做时，此函数会被调用。
        /// 
        /// 提请用户确认是否执行。如果确定执行，此次以及之前撤消不能重做。
        /// \param undoName 撤消操作的名称
        /// \return bool 返回true确认执行；false放弃。
        virtual bool ConfirmToExecuteUndoWithoutRedoCapability(
            const std::wstring & undoName) = 0;
    };

    /// \brief 找下一个非空Undo/Redo实体Id结果
    enum class ResultOfGetNextNotEmptyUndoRedoId : int32_t
    {
        Found = 0,                      /// < 找到了
        UndoOrRedoContainerIsEmpty = 1, /// < Undo/Redo容器为空
        AllUndoRedoEntityAreEmpty = 2,  /// < 所有的Undo/Redo实体都是空的
    };

    /// \brief UndoRedoUtils
    ///
    /// 撤消、还原工具
    class GCMP_MODEL_EXPORT UndoRedoUtils final
    {
    public:
        /// \brief 撤消上一步操作
        /// \param pDocument 文档指针
        /// \param pUndoConfirm 请用户确认执行undo
        /// \return  true表示撤消成功，false表示撤消失败
        static bool Undo(
            IDocument* pDocument, 
            IUndoConfirm * pUndoConfirm = nullptr
        );

        /// \brief 撤消操作到给定的undo Id
        /// \param pDocument 文档指针
        /// \param undoId 期望执行到的undo的Id
        /// \param pUndoConfirm 请用户确认执行undo
        /// \return  true表示撤消成功，false表示撤消失败，失败的情况包括pDocument为空，或者undoId不合法，或者中间任意一步失败
        static bool Undo(
            IDocument* pDocument,
            int32_t undoId,
            IUndoConfirm * pUndoConfirm = nullptr
        );

        /// \brief 是否有可以撤消的操作，一般还需要通过IUserTransaction::IsInUserTransaction判断是否在事务中来判断能否进行撤消
        /// \param pDocument 文档指针
        /// \return  true表示有可以撤消的操作，false表示没有可以撤消的操作
        static bool CanUndo(const IDocument* pDocument);

        /// \brief 获取可撤消操作列表
        /// \param pDocument 文档指针
        /// \return  返回可撤消操作列表
        static std::vector<std::wstring> GetUndoNames(const IDocument* pDocument);

        /// \brief 按撤消时执行顺序获取所有可撤消操作的摘要信息列表，摘要信息包括：Name，Id，IsEmpty
        /// \param pDocument 文档指针
        /// \return std::vector<UndoRedoEntityBrief> 所有可撤消操作的摘要信息列表，列表的头部是最近的一次可撤消操作的摘要信息
        static std::vector<UndoRedoEntityBrief> GetUndoEntityBriefs(const IDocument* pDocument);

        /// \brief 判断undo的数据实体是否为空
        /// \param pDocument 文档指针
        /// \param undoId undo的Id,这里的undoId不能任意给，从GetUndoEntityBriefs(const IDocument* pDocument)取值,否则返回false
        /// \return  bool false表示不为空，true表示为空
        static bool IsUndoEmpty(const IDocument* pDocument, int32_t undoId);

        /// \brief 获取下一个非空的undo Id
        /// \param pDocument 文档指针
        /// \param [out] resultOfGetNextNotEmptyUndoRedoId 获取下一个非空Undo/Redo实体Id的结果
        /// \return int32_t 返回下一个非空undo Id，如果容器为空或者每一个undo都是空返回-1
        /// \note 这是一个便利函数，可以通过GetUndoEntityBriefs(const IDocument* pDocument)实现
        static int32_t GetNextNotEmptyUndoId(const IDocument* pDocument, ResultOfGetNextNotEmptyUndoRedoId & resultOfGetNextNotEmptyUndoRedoId);

        /// \brief 重做一步操作
        /// \param pDocument 文档指针
        /// \return  true表示重做成功，false表示重做失败
        static bool Redo(IDocument* pDocument);

        /// \brief 重做操作到给定的redo Id
        /// \param pDocument 文档指针
        /// \param redoId 期望执行到的redo的Id
        /// \return  true表示重做成功，false表示重做失败，失败的情况包括pDocument为空，或者redoId不合法，或者中间任意一步失败
        static bool Redo(
            IDocument* pDocument,
            int32_t redoId
        );

        /// \brief 是否有可以重做的操作，一般还需要通过IUserTransaction::IsInUserTransaction判断是否在事务中来判断能否进行重做
        /// \param pDocument 文档指针
        /// \return  true表示有可以重做的操作，false表示没有可以重做的操作
        static bool CanRedo(const IDocument* pDocument);

        /// \brief 获取可重做操作列表
        /// \param pDocument 文档指针
        /// \return  返回可重做操作列表
        static std::vector<std::wstring> GetRedoNames(const IDocument* pDocument);

        /// \brief 按重做时执行顺序获取所有可重做操作的摘要信息列表，摘要信息包括：Name，Id，IsEmpty
        /// \param pDocument 文档指针
        /// \return std::vector<UndoRedoEntityBrief> 所有可重做操作的摘要信息列表，列表的头部是最近的一次可重做操作的摘要信息
        static std::vector<UndoRedoEntityBrief> GetRedoEntityBriefs(const IDocument* pDocument);

        /// \brief 判断redo的数据实体是否为空
        /// \param pDocument 文档指针
        /// \param redoId redo的Id，这里的redoId不能任意给，从 GetRedoEntityBriefs(const IDocument* pDocument)取值,否则返回false
        /// \return  bool false表示不为空，true表示为空
        static bool IsRedoEmpty(const IDocument* pDocument, int32_t redoId);

        /// \brief 获取下一个非空的redo Id
        /// \param pDocument 文档指针
        /// \param [out] resultOfGetNextNotEmptyUndoRedoId 获取下一个非空Undo/Redo实体Id的结果
        /// \return int32_t 返回下一个非空redo Id，如果容器为空或者每一个redo都是空返回-1
        /// \note 这是一个便利函数，可以通过GetRedoEntityBriefs(const IDocument* pDocument)实现
        static int32_t GetNextNotEmptyRedoId(const IDocument* pDocument, ResultOfGetNextNotEmptyUndoRedoId & resultOfGetNextNotEmptyUndoRedoId);

        /// \brief 获取最后一个事务的ElementId信息
        ///
        /// 如果最后一个事务为事务组，也只获取最后一个事务组里的事务的ElementId信息
        /// \param pDocument 文档指针
        /// \param [out] added 最后一个事务中增加的的ElementId信息
        /// \param [out] deleted 最后一个事务中删除的的ElementId信息
        /// \param [out] updated 最后一个事务中修改的的ElementId信息
        /// \param [out] loaded 最后一个事务中加载的的ElementId信息
        /// \param [out] unloaded 最后一个事务中卸载的的ElementId信息
        /// \param [out] pErrorMessage 返回的错误信息
        static bool GetElementIdsOfLastStoredUndo(
            const IDocument * pDocument,
            std::vector<ElementId>& added,
            std::vector<ElementId>& deleted,
            std::vector<ElementId>& updated,
            std::vector<ElementId>& loaded,
            std::vector<ElementId>& unloaded,
            std::wstring * pErrorMessage
        );

        /// \brief 清除给定UndoId及之前的undo操作（包括工作目录下的undo文件和文档中的undoBrief信息）
        ///
        /// 在文档打开时，调用该接口清除指定的undo操作，调用后，该undoId及之前的操作将不再支持撤销
        /// 1）传入的undoId必须从接口GetUndoEntityBriefs(const IDocument* pDocument)中得到
        /// 2）该接口仅供崩溃恢复走协同端的产品使用。如果使用本地文件进行崩溃恢复的，禁止调用该接口！
        ///
        /// \param pDocument 文档指针
        /// \param undoId undo的Id。该Id须为当前可撤销的操作列表中的Id
        /// \param [out] pErrorMessage 返回的错误信息
        /// \return true表示清除成功；false表示清除失败。失败的情况包括pDocument为空，或者undoId不合法，或者中间任意一步失败
        static bool ClearUndos(IDocument* pDocument, int32_t undoId, std::wstring* pErrorMessage);

        /// \brief 撤销指定的事务和之后的事务，并删除相应的重做动作
        ///
        /// \param pDocument 文档指针
        /// \param transactionId 事务Id。-1也是一个合法值，表明之前没有可以撤销的事务，此API也能正常工作。
        /// \param [out] pErrorMessage 返回的错误信息
        /// \return true-成功；false-失败。失败的情况包括pDocument为空，或者其它失败
        static bool UndoSuccessorsAndClearRedo(
            IDocument* pDocument,
            int32_t transactionId,
            std::wstring* pErrorMessage
        );
    };
}